package myCine.alloCine.controller;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import myCine.alloCine.model.app.criteriaOfSearch.MovieCriteria;
import myCine.alloCine.model.app.result.SearchResult;
import myCine.alloCine.model.exchange.search.Search;
import myCine.alloCine.model.exchange.search.subClass.Movie;
import utils.ComparatorsStock;
import utils.StringUtils;

public class SearchManager extends myCine.global.controller.GlobalSearchManager {
	
	
	/*============================
	 * 	BEFORE CALLING SERVICES
	 *============================*/

	public SearchResult getListOfMovies(final List<String> listOfTitles) throws Exception{
		return getListOfMovies(listOfTitles, null, ComparatorsStock.DEFAULT_MOVIE_COMPARATOR);
	}
	
	
	public SearchResult getListOfMovies(
			final List<String> listOfTitles,
			final MovieCriteria movieCriteria) throws Exception{
		return getListOfMovies(listOfTitles, movieCriteria, ComparatorsStock.DEFAULT_MOVIE_COMPARATOR);
	}
	

	public SearchResult getListOfMovies(
			final List<String> listOfTitles,
			final MovieCriteria movieCriteria,
			final Comparator<Movie> comparator) throws Exception{
		
		SearchResult searchResult = new SearchResult();
		
		for(String title : listOfTitles){
			
			Movie movie = getBestMovie(title, movieCriteria, comparator);
			
			if(movie != null){
				
				if(!movie.isBadCriteria()){ //if criteria have been respected
					searchResult.getListOfMovies().add(movie); //keep movie
				} else { //else place in other list
					searchResult.getListOfMoviesBadCriteria().add(movie);
				}
			} else {
				searchResult.getListOfMoviesNotFound().add(title.trim());
			}
		}
		
		return searchResult;
	}
	

	/*=========================
	 * 		CALL SERVICES
	 *=========================*/
	
	/**
 	 * If more than one movie is returned, list of results is sorted,
 	 * and the first movie is returned. If comparator  is null, the
 	 * first result is returned without comparision.
	 * @param name movie name

	 * @return return one movie or null if 0 movie found
	 * @throws Exception 
	 */
	private List<Movie> getSearchedMovies(String name)
			throws Exception{
		
		List<Movie> listeOfMoviesSearch = new ArrayList<Movie>();

		Search resultSearch = Services.searchMovies(name);
		
		if(resultSearch != null){
		
			//If we have one movie
			if(resultSearch.getMovie() != null)
			{
				Integer movieCode = resultSearch.getMovie().getCode();
				
				//get details about the film
				if(movieCode != null){
					
					Movie movieDetails = getMovieDetails(movieCode);
					
					if(movieDetails != null){
						listeOfMoviesSearch.add(movieDetails);
					}
				}
				
			} else if(resultSearch.getFeed() != null
					&& resultSearch.getFeed().getMovie() != null
					&& !resultSearch.getFeed().getMovie().isEmpty())
			{
				//get details for each film
				List<Movie> listOfMoviesDetails = getMoviesDetails(resultSearch.getFeed().getMovie());
				listeOfMoviesSearch.addAll(listOfMoviesDetails);
			}

		}
		
		return listeOfMoviesSearch;	
	}
	
	
	
	/**
	 * Get details about movie
	 */
	protected Movie getMovieDetails(final Integer movieCode) throws Exception{
		return Services.movieDetails(movieCode);
	}
	
	private List<Movie> getMoviesDetails(final List<Movie> listOfMovies) throws Exception{
		
		List<Movie> listOfMoviesDetails = new ArrayList<Movie>();
		
		for(Movie movie : listOfMovies){
			
			if(movie.getCode() != null){
				Movie movieDetails = getMovieDetails(movie.getCode());
				if(movieDetails != null){
					listOfMoviesDetails.add(movieDetails);
				}
			}
		}
		
		return listOfMoviesDetails;
	}
	
	
	/*===============================
	 * 	PROCESSING OF DATA RETURNED
	 *===============================*/
	
	
	public Movie getBestMovie(String title, MovieCriteria movieCritera) throws Exception{
		return getBestMovie(title,movieCritera, ComparatorsStock.YOUNGEST_AND_BEST_RATED_MOVIE);
	}
	
	/**
	 * Search one film which respect given criteria
	 * @param movieTitle
	 * @param movieCritera all critera to respect
	 * @param comparator a movie comparator, can be null = no comparision
	 * @throws Exception 
	 */
	public Movie getBestMovie(String movieTitle, MovieCriteria movieCritera, Comparator<Movie> comparator) throws Exception{
		
		List<Movie> listeOfMovies = null;
		Movie movie = null;
		
		//We get the movie, or a list of movies
		if(StringUtils.isNotEmpty(movieTitle)){
			listeOfMovies = getSearchedMovies(getPreparedNameForSearch(movieTitle));
		}
		
		if(listeOfMovies != null){
			
			//Sort list
			if(comparator != null){
				Collections.sort(listeOfMovies, comparator);
			}
			
			
			if(movieCritera != null){
				
				//Delete all movies which doesn't respect critera
				for (Iterator<Movie> iterator = listeOfMovies.iterator(); iterator.hasNext();)
				{
					
					boolean badCriteria = false;
					Movie movieSearch = (Movie) iterator.next();
					
					//Production Year
					if(movieCritera.getProductionYearEqualOrHigher() != null){
						if(movieSearch.getProductionYear() == null
								|| (movieSearch.getProductionYear() < movieCritera.getProductionYearEqualOrHigher())){
							badCriteria = true;
						}
					}
					
					//Rate
					if(!badCriteria && movieCritera.getUserRatingEqualOrHigher() != null){
						if(movieSearch.getStatistics() == null
								|| movieSearch.getStatistics().getUserRating() == null
								|| movieSearch.getStatistics().getUserRating() < movieCritera.getUserRatingEqualOrHigher()){
							badCriteria = true;
						}
					}
					
					if(!badCriteria && movieCritera.getUserRatingCountEqualOrHigher() != null){
						if(movieSearch.getStatistics() == null
								|| movieSearch.getStatistics().getUserRatingCount() == null
								|| movieSearch.getStatistics().getUserRatingCount() < movieCritera.getUserRatingCountEqualOrHigher()){
							badCriteria = true;
						}
					}
					
					//Set the value, could be true or false
					movieSearch.setBadCriteria(badCriteria);
					
				}
				
			}

			if(!listeOfMovies.isEmpty()){
				//Keep first movie
				movie = listeOfMovies.get(0);
				decodeMovieString(movie);
			}
		}
		
		return movie;
	}
	
	

	
}
